package com.boot.security.service.authentication;

import com.boot.web.util.HttpResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @date 2021/1/18 20:48
 **/
@Slf4j
public class LogoutFilter extends GenericFilterBean {

    private final RequestMatcher logoutRequestMatcher;

    private final TokenService tokenService;

    private final LogoutSuccessHandler logoutSuccessHandler;

    private final LogoutFailureHandler logoutFailureHandler;


    public LogoutFilter(RequestMatcher logoutRequestMatcher, TokenService tokenService, LogoutSuccessHandler logoutSuccessHandler, LogoutFailureHandler logoutFailureHandler) {
        this.logoutRequestMatcher = logoutRequestMatcher;
        this.tokenService = tokenService;
        this.logoutSuccessHandler = logoutSuccessHandler;
        this.logoutFailureHandler = logoutFailureHandler;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
    }

    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (ignoreRequest(request)) {
            chain.doFilter(request, response);
        } else {
            log.info("======================================== LogoutFilter ======================================");
            String token = request.getHeader(TokenService.AUTHORIZATION);
            if (StringUtils.hasText(token)) {
                try {
                    tokenService.invalid(tokenService.parse(token));
                } catch (AuthenticationException e) {
                    this.logoutFailureHandler.onLogoutFailure(request, response, e);
                }
            } else {
                log.warn("未携带凭据");
            }
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            this.logoutSuccessHandler.onLogoutSuccess(request, response, auth);
        }
    }

    protected boolean ignoreRequest(HttpServletRequest request) {
        if (this.logoutRequestMatcher.matches(request)) {
            return false;
        }
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(LogMessage.format("Did not match request to %s", this.logoutRequestMatcher));
        }
        return true;
    }

    @Slf4j
    public static class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {

        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            String msg = String.format("用户 %s 登出成功", authentication.getName());
            log.info(msg);
            //            SecurityContextHolder.clearContext();
            HttpResponseUtil.writeResponse(response, HttpResponseUtil.OK(msg));
        }
    }

    public static interface LogoutFailureHandler {
        void onLogoutFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException;
    }

    @Slf4j
    public static class LogoutFailureHandlerImpl implements LogoutFailureHandler {

        @Override
        public void onLogoutFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            log.info("未知用户试图登出,错误信息：{}", exception.getMessage());
            HttpResponseUtil.writeResponse(response, HttpResponseUtil.unAuthorized(exception.getMessage()));
        }
    }
}
